﻿using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using SmartMathLibrary.SQLServer.Commands;

public partial class UserDefinedFunctions
{
    /// <summary>
    /// Transforms a number to an always positive value.
    /// </summary>
    /// <param name="number">The number to fab.</param>
    /// <returns>The absolute value of a number.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double Fabs(double number)
    {
        if (number < 0)
        {
            return -number;
        }

        return number;
    }

    /// <summary>
    /// Searches the greatest common divisor of two numbers.
    /// </summary>
    /// <param name="number1">The first number.</param>
    /// <param name="number2">The second number.</param>
    /// <returns>The greatest common divisor.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static long GCD(long number1, long number2)
    {
        long remainder = 0;

        do
        {
            remainder = number1%number2;
            number1 = number2;
            number2 = remainder;
        } while (remainder != 0);

        return number1;
    }

    /// <summary>
    /// Returns the remainder of the specified two numbers.
    /// </summary>
    /// <param name="a">The first number.</param>
    /// <param name="b">The second number.</param>
    /// <returns>Returns the remainder of the specified two numbers.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static int Remainder(int a, int b)
    {
        if (a < b)
        {
            return 0;
        }

        return (a - (a%b))/b;
    }

    /// <summary>
    /// Computes the modular multiplicative inverse of two numbers. If the inverse does not 
    /// exist, it returns -1.
    /// </summary>
    /// <param name="a">The first number.</param>
    /// <param name="b">The second number</param>
    /// <returns>Returns the modular multiplicative inverse of two numbers. If the inverse does 
    /// not exist, it returns -1.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static int ModularMultiplicativeInverse(int a, int b)
    {
        int x = 0;
        int y = 1;
        int lastx = 1;
        int lasty = 0;

        while (b != 0)
        {
            int tempuri = b;
            int quotient = Remainder(a, b);

            b = a%b;
            a = tempuri;
            tempuri = x;
            x = lastx - quotient*x;
            lastx = tempuri;
            tempuri = y;
            y = lasty - quotient*y;
            lasty = tempuri;
        }

        if (a == 1)
        {
            return lastx;
        }

        return -1;
    }

    /// <summary>
    /// Gets the smallest common multiplier if two numbers.
    /// </summary>
    /// <param name="number1">The first number.</param>
    /// <param name="number2">The second number.</param>
    /// <returns>The smallest common multiplier.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static long SCM(long number1, long number2)
    {
        return (number1*number1)/GCD(number1, number2);
    }

    /// <summary>
    /// Computes the n-th root of a number.
    /// </summary>
    /// <param name="number">The number.</param>
    /// <param name="root">The specified root.</param>
    /// <returns>Returns the n-th root of a number.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double Root(double number, double root)
    {
        return Math.Pow(number, 1/root);
    }

    /// <summary>
    /// Converts an angle from radian to degree.
    /// </summary>
    /// <param name="rad">The radian value.</param>
    /// <returns>The converted angle.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double ToDegree(double rad)
    {
        return 180/Math.PI*rad;
    }

    /// <summary>
    /// Converts an angle from degree to radian.
    /// </summary>
    /// <param name="deg">The degree value.</param>
    /// <returns>The converted angle.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double ToRadians(double deg)
    {
        return deg/180*Math.PI;
    }

    /// <summary>
    /// Computes the hypotenuse of an orthogonal triangle (a² + b² = c²).
    /// </summary>
    /// <param name="a">The first cathetus a.</param>
    /// <param name="b">The second cathetus b.</param>
    /// <returns>The hypotenuse of an orthogonal triangle c.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double Phytagoras2D(double a, double b)
    {
        return Math.Sqrt(Math.Pow(a, 2) + Math.Pow(b, 2));
    }

    /// <summary>
    /// Computes the hypotenuse of an three dimensional triangle (a² + b² + c²= d²).
    /// </summary>
    /// <param name="a">The first cathetus a.</param>
    /// <param name="b">The second cathetus b.</param>
    /// <param name="c">The third cathetus c.</param>
    /// <returns>The hypotenuse of an three dimensional triangle d.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double Phytagoras3D(double a, double b, double c)
    {
        return Math.Sqrt(Math.Pow(a, 2) + Math.Pow(b, 2) + Math.Pow(c, 2));
    }

    /// <summary>
    /// Determines whether the specified value is an integer.
    /// </summary>
    /// <param name="value">The value to check.</param>
    /// <returns>
    /// 	<c>true</c> if the specified value is integer; otherwise, <c>false</c>.
    /// </returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static bool IsInteger(double value)
    {
        return Math.Ceiling(value) == 0;
    }

    /// <summary>
    /// Determines whether the specified value is an even number.
    /// </summary>
    /// <param name="value">The value to check.</param>
    /// <returns>
    /// 	<c>true</c> if the specified value is even; otherwise, <c>false</c>.
    /// </returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static bool IsEven(int value)
    {
        return value%2 == 0;
    }

    /// <summary>
    /// Determines whether the specified value is an odd number.
    /// </summary>
    /// <param name="value">The value to check.</param>
    /// <returns>
    /// 	<c>true</c> if the specified value is odd; otherwise, <c>false</c>.
    /// </returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static bool IsOdd(int value)
    {
        return !IsEven(value);
    }

    /// <summary>
    /// Determines whether the specified number is a square number.
    /// </summary>
    /// <param name="number">The number to check.</param>
    /// <returns>
    /// True if the specified number is a square number otherwise, false.
    /// </returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static bool IsSquareNumber(double number)
    {
        return (Math.Sqrt(number) == Math.Ceiling(Math.Sqrt(number)));
    }

    /// <summary>
    /// The mathematic binomial coefficient operation.
    /// </summary>
    /// <param name="n">The first value (n).</param>
    /// <param name="k">The second value (k).</param>
    /// <returns>The computed value of the binomial coefficient.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlInt64 BinomialCoefficient(SqlInt64 n, SqlInt64 k)
    {
        if (Factorials.Factorial((uint) k)*Factorials.Factorial((uint) n - (uint) k) == 0u)
        {
            throw new ArgumentException("complex reason");
        }

        uint factorialN = Factorials.Factorial((uint) n);
        uint factorialK = Factorials.Factorial((uint) k);
        uint factorialY = Factorials.Factorial((uint) n - (uint) k);

        return (factorialN/(factorialK*factorialY));
    }

    /// <summary>
    /// Checks if the quadratic equation has a complex result.
    /// </summary>
    /// <param name="p">The p value.</param>
    /// <param name="q">The q value.</param>
    /// <returns>True if the result is complex otherwise, false.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static bool QuadraticEquationHasComplexResult(double p, double q)
    {
        return (Math.Pow(p/2, 2) - q) < 0;
    }

    /// <summary>
    /// Herons formula to calculate the are of a triangle by specifying the three sides.
    /// </summary>
    /// <param name="a">The first side of the triangle.</param>
    /// <param name="b">The second side of the triangle.</param>
    /// <param name="c">The third side of the triangle.</param>
    /// <returns>The area of the triangle.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double HeronFormula(double a, double b, double c)
    {
        double s = (a + b + c)/2;

        return Math.Sqrt(s*(s - a)*(s - b)*(s - c));
    }

    /// <summary>
    /// Calculates the area of a general quadrilateral by using the Brahmagupta formula.
    /// </summary>
    /// <param name="a">The length of the first side a.</param>
    /// <param name="b">The length of the second side b.</param>
    /// <param name="c">The length of the third side c.</param>
    /// <param name="d">The length of the fourth side d.</param>
    /// <param name="angleA">The angle between the sides a and d.</param>
    /// <param name="angleB">The angle between the sides b and c.</param>
    /// <returns>The area of the specified general quadrilateral.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double BrahmaguptaFormula(double a, double b, double c, double d,
                                            double angleA, double angleB)
    {
        double s = 0.5*(a + b + c + d);

        return Math.Sqrt((s - a)*(s - b)*(s - c)*(s - d) - a*b*c*d
                                                           *Math.Pow(Math.Cos(0.5*(angleA + angleB)), 2));
    }

    /// <summary>
    /// Calculates the area of a general quadrilateral by using the Bretschneider formula.
    /// </summary>
    /// <param name="a">The length of the first side a.</param>
    /// <param name="b">The length of the second side b.</param>
    /// <param name="c">The length of the third side c.</param>
    /// <param name="d">The length of the fourth side d.</param>
    /// <param name="p">The first diagonal length p between a and c.</param>
    /// <param name="q">The first diagonal length q between b and d.</param>
    /// <returns>The area of the specified general quadrilateral.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double BretschneiderFormula(double a, double b, double c, double d,
                                              double p, double q)
    {
        return 0.25*Math.Sqrt(4*Math.Pow(p, 2)*Math.Pow(q, 2)
                              - (Math.Pow(Math.Pow(b, 2) + Math.Pow(d, 2) - Math.Pow(a, 2) - Math.Pow(c, 2), 2)));
    }

    /// <summary>
    /// Calculates the distance between two points in a plane space.
    /// </summary>
    /// <param name="x1">The x value of the first point.</param>
    /// <param name="y1">The y value of the first point</param>
    /// <param name="x2">The x value of the second point</param>
    /// <param name="y2">The y value of the second point</param>
    /// <returns>
    /// The plane distance between the specified points.
    /// </returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double CalculateDistance(double x1, double y1, double x2, double y2)
    {
        return Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2));
    }

    /// <summary>
    ///Computes the hyperbolic secant at the specified value x.
    /// </summary>
    /// <param name="x">The value for evaluation in radians.</param>
    /// <returns>The result of the function at the specified position x.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double Sech(double x)
    {
        return 1/Math.Cosh(x);
    }

    /// <summary>
    /// Computes the hyperbolic cosecant at the specified value x.
    /// </summary>
    /// <param name="x">The value for evaluation in radians.</param>
    /// <returns>The result of the function at the specified position x.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double Csch(double x)
    {
        return 1/Math.Sinh(x);
    }

    /// <summary>
    /// Computes the arcussecans at the specified value x.
    /// </summary>
    /// <param name="x">The value for evaluation in radians.</param>
    /// <returns>The result of the function at the specified position x.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double Asec(double x)
    {
        return Math.Acos(1/x);
    }

    /// <summary>
    /// Computes the arkuscosecans at the specified value x.
    /// </summary>
    /// <param name="x">The value for evaluation in radians.</param>
    /// <returns>The result of the function at the specified position x.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double Acsc(double x)
    {
        return Math.Asin(1/x);
    }

    /// <summary>
    /// Computes the areasinus hyperbolicus at the specified value x.
    /// </summary>
    /// <param name="x">The value for evaluation in radians.</param>
    /// <returns>The result of the function at the specified position x.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double Asinh(double x)
    {
        return Math.Log(x + Math.Sqrt(Math.Pow(x, 2) + 1));
    }

    /// <summary>
    /// Computes the areacosinus hyperbolicus at the specified value x.
    /// </summary>
    /// <param name="x">The value for evaluation in radians.</param>
    /// <returns>The result of the function at the specified position x.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double Acosh(double x)
    {
        return Math.Log(x + Math.Sqrt(Math.Pow(x, 2) - 1));
    }

    /// <summary>
    /// Computes the areatangens hyperbolicus at the specified value x. This function is defined
    /// for |x| smaller 1.
    /// </summary>
    /// <param name="x">The value for evaluation in radians.</param>
    /// <returns>The result of the function at the specified position x.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double Atanh(double x)
    {
        if (x == 0)
        {
            return 0;
        }

        return 0.5*Math.Log((1 + x)/(1 - x));
    }

    /// <summary>
    /// Computes the areakotangens hyperbolicus at the specified value x. This function is defined
    /// for |x| greater 1.
    /// </summary>
    /// <param name="x">The value for evaluation in radians.</param>
    /// <returns>The result of the function at the specified position x.</returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static double Acoth(double x)
    {
        if (x == 0)
        {
            return 0;
        }

        return 0.5*Math.Log((1 + x)/(1 - x));
    }
} ;